4fca00237a836eab4336617a84e408979b7ddb1f
3 class LanguageTest
extends LanguageClassesTestCase
{
5 * @covers Language::convertDoubleWidth
6 * @covers Language::normalizeForSearch
8 public function testLanguageConvertDoubleWidthToSingleWidth() {
10 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
11 $this->getLang()->normalizeForSearch(
12 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
14 'convertDoubleWidth() with the full alphabet and digits'
19 * @dataProvider provideFormattableTimes
20 * @covers Language::formatTimePeriod
22 public function testFormatTimePeriod( $seconds, $format, $expected, $desc ) {
23 $this->assertEquals( $expected, $this->getLang()->formatTimePeriod( $seconds, $format ), $desc );
26 public static function provideFormattableTimes() {
32 'formatTimePeriod() rounding (<10s)'
36 array( 'noabbrevs' => true ),
38 'formatTimePeriod() rounding (<10s)'
44 'formatTimePeriod() rounding (<10s)'
48 array( 'noabbrevs' => true ),
50 'formatTimePeriod() rounding (<10s)'
56 'formatTimePeriod() rounding (<60s)'
60 array( 'noabbrevs' => true ),
62 'formatTimePeriod() rounding (<60s)'
68 'formatTimePeriod() rounding (<1h)'
72 array( 'noabbrevs' => true ),
73 '2 minutes 0 seconds',
74 'formatTimePeriod() rounding (<1h)'
80 'formatTimePeriod() rounding (<1h)'
84 array( 'noabbrevs' => true ),
85 '1 hour 0 minutes 0 seconds',
86 'formatTimePeriod() rounding (<1h)'
92 'formatTimePeriod() rounding (>=1h)'
96 array( 'noabbrevs' => true ),
97 '2 hours 0 minutes 0 seconds',
98 'formatTimePeriod() rounding (>=1h)'
104 'formatTimePeriod() rounding (>=1h), avoidseconds'
108 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
110 'formatTimePeriod() rounding (>=1h), avoidseconds'
116 'formatTimePeriod() rounding (>=1h), avoidminutes'
120 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
122 'formatTimePeriod() rounding (>=1h), avoidminutes'
128 'formatTimePeriod() rounding (=48h), avoidseconds'
132 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
133 '48 hours 0 minutes',
134 'formatTimePeriod() rounding (=48h), avoidseconds'
140 'formatTimePeriod() rounding (>48h), avoidminutes'
144 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
146 'formatTimePeriod() rounding (>48h), avoidminutes'
152 'formatTimePeriod() rounding (>48h), avoidseconds'
156 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
157 '2 days 1 hour 0 minutes',
158 'formatTimePeriod() rounding (>48h), avoidseconds'
164 'formatTimePeriod() rounding (>48h), avoidminutes'
168 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
170 'formatTimePeriod() rounding (>48h), avoidminutes'
176 'formatTimePeriod() rounding (>48h), avoidseconds'
180 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
181 '3 days 0 hours 0 minutes',
182 'formatTimePeriod() rounding (>48h), avoidseconds'
188 'formatTimePeriod() rounding, (>48h), avoidseconds'
192 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
193 '2 days 0 hours 0 minutes',
194 'formatTimePeriod() rounding, (>48h), avoidseconds'
200 'formatTimePeriod() rounding, recursion, (>48h)'
204 array( 'noabbrevs' => true ),
205 '2 days 1 hour 1 minute 1 second',
206 'formatTimePeriod() rounding, recursion, (>48h)'
212 * @covers Language::truncate
214 public function testTruncate() {
217 $this->getLang()->truncate( "1234567890", 0, 'XXX' ),
218 'truncate prefix, len 0, small ellipsis'
223 $this->getLang()->truncate( "1234567890", 8, 'XXX' ),
224 'truncate prefix, small ellipsis'
229 $this->getLang()->truncate( "123456789", 5, 'XXXXXXXXXXXXXXX' ),
230 'truncate prefix, large ellipsis'
235 $this->getLang()->truncate( "1234567890", -8, 'XXX' ),
236 'truncate suffix, small ellipsis'
241 $this->getLang()->truncate( "123456789", -5, 'XXXXXXXXXXXXXXX' ),
242 'truncate suffix, large ellipsis'
246 $this->getLang()->truncate( "123 ", 9, 'XXX' ),
247 'truncate prefix, with spaces'
251 $this->getLang()->truncate( "12345 8", 11, 'XXX' ),
252 'truncate prefix, with spaces and non-space ending'
256 $this->getLang()->truncate( "1 234", -8, 'XXX' ),
257 'truncate suffix, with spaces'
261 $this->getLang()->truncate( "1234567890", 5, 'XXX', false ),
262 'truncate without adjustment'
267 * @dataProvider provideHTMLTruncateData
268 * @covers Language::truncateHTML
270 public function testTruncateHtml( $len, $ellipsis, $input, $expected ) {
274 $this->getLang()->truncateHTML( $input, $len, $ellipsis )
279 * @return array Format is ($len, $ellipsis, $input, $expected)
281 public static function provideHTMLTruncateData() {
283 array( 0, 'XXX', "1234567890", "XXX" ),
284 array( 8, 'XXX', "1234567890", "12345XXX" ),
285 array( 5, 'XXXXXXXXXXXXXXX', '1234567890', "1234567890" ),
287 '<p><span style="font-weight:bold;"></span></p>',
288 '<p><span style="font-weight:bold;"></span></p>',
291 '<p><span style="font-weight:bold;">123456789</span></p>',
292 '<p><span style="font-weight:bold;">***</span></p>',
295 '<p><span style="font-weight:bold;"> 23456789</span></p>',
296 '<p><span style="font-weight:bold;">***</span></p>',
299 '<p><span style="font-weight:bold;">123456789</span></p>',
300 '<p><span style="font-weight:bold;">***</span></p>',
303 '<p><span style="font-weight:bold;">123456789</span></p>',
304 '<p><span style="font-weight:bold;">1***</span></p>',
307 '<tt><span style="font-weight:bold;">123456789</span></tt>',
308 '<tt><span style="font-weight:bold;">12***</span></tt>',
311 '<p><a href="www.mediawiki.org">123456789</a></p>',
312 '<p><a href="www.mediawiki.org">123***</a></p>',
315 '<p><a href="www.mediawiki.org">12 456789</a></p>',
316 '<p><a href="www.mediawiki.org">12 ***</a></p>',
319 '<small><span style="font-weight:bold;">123<p id="#moo">456</p>789</span></small>',
320 '<small><span style="font-weight:bold;">123<p id="#moo">4***</p></span></small>',
323 '<div><span style="font-weight:bold;">123<span>4</span>56789</span></div>',
324 '<div><span style="font-weight:bold;">123<span>4</span>5***</span></div>',
327 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
328 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
331 '<p><font style="font-weight:bold;">123456789</font></p>',
332 '<p><font style="font-weight:bold;">123456789</font></p>',
338 * Test Language::isWellFormedLanguageTag()
339 * @dataProvider provideWellFormedLanguageTags
340 * @covers Language::isWellFormedLanguageTag
342 public function testWellFormedLanguageTag( $code, $message = '' ) {
344 Language
::isWellFormedLanguageTag( $code ),
345 "validating code $code $message"
350 * The test cases are based on the tests in the GaBuZoMeu parser
351 * written by Stéphane Bortzmeyer <bortzmeyer@nic.fr>
352 * and distributed as free software, under the GNU General Public Licence.
353 * http://www.bortzmeyer.org/gabuzomeu-parsing-language-tags.html
355 public static function provideWellFormedLanguageTags() {
357 array( 'fr', 'two-letter code' ),
358 array( 'fr-latn', 'two-letter code with lower case script code' ),
359 array( 'fr-Latn-FR', 'two-letter code with title case script code and uppercase country code' ),
360 array( 'fr-Latn-419', 'two-letter code with title case script code and region number' ),
361 array( 'fr-FR', 'two-letter code with uppercase' ),
362 array( 'ax-TZ', 'Not in the registry, but well-formed' ),
363 array( 'fr-shadok', 'two-letter code with variant' ),
364 array( 'fr-y-myext-myext2', 'non-x singleton' ),
365 array( 'fra-Latn', 'ISO 639 can be 3-letters' ),
366 array( 'fra', 'three-letter language code' ),
367 array( 'fra-FX', 'three-letter language code with country code' ),
368 array( 'i-klingon', 'grandfathered with singleton' ),
369 array( 'I-kLINgon', 'tags are case-insensitive...' ),
370 array( 'no-bok', 'grandfathered without singleton' ),
371 array( 'i-enochian', 'Grandfathered' ),
372 array( 'x-fr-CH', 'private use' ),
373 array( 'es-419', 'two-letter code with region number' ),
374 array( 'en-Latn-GB-boont-r-extended-sequence-x-private', 'weird, but well-formed' ),
375 array( 'ab-x-abc-x-abc', 'anything goes after x' ),
376 array( 'ab-x-abc-a-a', 'anything goes after x, including several non-x singletons' ),
377 array( 'i-default', 'grandfathered' ),
378 array( 'abcd-Latn', 'Language of 4 chars reserved for future use' ),
379 array( 'AaBbCcDd-x-y-any-x', 'Language of 5-8 chars, registered' ),
380 array( 'de-CH-1901', 'with country and year' ),
381 array( 'en-US-x-twain', 'with country and singleton' ),
382 array( 'zh-cmn', 'three-letter variant' ),
383 array( 'zh-cmn-Hant', 'three-letter variant and script' ),
384 array( 'zh-cmn-Hant-HK', 'three-letter variant, script and country' ),
385 array( 'xr-p-lze', 'Extension' ),
390 * Negative test for Language::isWellFormedLanguageTag()
391 * @dataProvider provideMalformedLanguageTags
392 * @covers Language::isWellFormedLanguageTag
394 public function testMalformedLanguageTag( $code, $message = '' ) {
396 Language
::isWellFormedLanguageTag( $code ),
397 "validating that code $code is a malformed language tag - $message"
402 * The test cases are based on the tests in the GaBuZoMeu parser
403 * written by Stéphane Bortzmeyer <bortzmeyer@nic.fr>
404 * and distributed as free software, under the GNU General Public Licence.
405 * http://www.bortzmeyer.org/gabuzomeu-parsing-language-tags.html
407 public static function provideMalformedLanguageTags() {
409 array( 'f', 'language too short' ),
410 array( 'f-Latn', 'language too short with script' ),
411 array( 'xr-lxs-qut', 'variants too short' ), # extlangS
412 array( 'fr-Latn-F', 'region too short' ),
413 array( 'a-value', 'language too short with region' ),
414 array( 'tlh-a-b-foo', 'valid three-letter with wrong variant' ),
417 'grandfathered but not registered: invalid, even if we only test well-formedness'
419 array( 'abcdefghi-012345678', 'numbers too long' ),
420 array( 'ab-abc-abc-abc-abc', 'invalid extensions' ),
421 array( 'ab-abcd-abc', 'invalid extensions' ),
422 array( 'ab-ab-abc', 'invalid extensions' ),
423 array( 'ab-123-abc', 'invalid extensions' ),
424 array( 'a-Hant-ZH', 'short language with valid extensions' ),
425 array( 'a1-Hant-ZH', 'invalid character in language' ),
426 array( 'ab-abcde-abc', 'invalid extensions' ),
427 array( 'ab-1abc-abc', 'invalid characters in extensions' ),
428 array( 'ab-ab-abcd', 'invalid order of extensions' ),
429 array( 'ab-123-abcd', 'invalid order of extensions' ),
430 array( 'ab-abcde-abcd', 'invalid extensions' ),
431 array( 'ab-1abc-abcd', 'invalid characters in extensions' ),
432 array( 'ab-a-b', 'extensions too short' ),
433 array( 'ab-a-x', 'extensions too short, even with singleton' ),
434 array( 'ab--ab', 'two separators' ),
435 array( 'ab-abc-', 'separator in the end' ),
436 array( '-ab-abc', 'separator in the beginning' ),
437 array( 'abcd-efg', 'language too long' ),
438 array( 'aabbccddE', 'tag too long' ),
439 array( 'pa_guru', 'A tag with underscore is invalid in strict mode' ),
440 array( 'de-f', 'subtag too short' ),
445 * Negative test for Language::isWellFormedLanguageTag()
446 * @covers Language::isWellFormedLanguageTag
448 public function testLenientLanguageTag() {
450 Language
::isWellFormedLanguageTag( 'pa_guru', true ),
451 'pa_guru is a well-formed language tag in lenient mode'
456 * Test Language::isValidBuiltInCode()
457 * @dataProvider provideLanguageCodes
458 * @covers Language::isValidBuiltInCode
460 public function testBuiltInCodeValidation( $code, $expected, $message = '' ) {
461 $this->assertEquals( $expected,
462 (bool)Language
::isValidBuiltInCode( $code ),
463 "validating code $code $message"
467 public static function provideLanguageCodes() {
469 array( 'fr', true, 'Two letters, minor case' ),
470 array( 'EN', false, 'Two letters, upper case' ),
471 array( 'tyv', true, 'Three letters' ),
472 array( 'tokipona', true, 'long language code' ),
473 array( 'be-tarask', true, 'With dash' ),
474 array( 'be-x-old', true, 'With extension (two dashes)' ),
475 array( 'be_tarask', false, 'Reject underscores' ),
480 * Test Language::isKnownLanguageTag()
481 * @dataProvider provideKnownLanguageTags
482 * @covers Language::isKnownLanguageTag
484 public function testKnownLanguageTag( $code, $message = '' ) {
486 (bool)Language
::isKnownLanguageTag( $code ),
487 "validating code $code - $message"
491 public static function provideKnownLanguageTags() {
493 array( 'fr', 'simple code' ),
494 array( 'bat-smg', 'an MW legacy tag' ),
495 array( 'sgs', 'an internal standard MW name, for which a legacy tag is used externally' ),
500 * @covers Language::isKnownLanguageTag
502 public function testKnownCldrLanguageTag() {
503 if ( !class_exists( 'LanguageNames' ) ) {
504 $this->markTestSkipped( 'The LanguageNames class is not available. '
505 . 'The CLDR extension is probably not installed.' );
509 (bool)Language
::isKnownLanguageTag( 'pal' ),
510 'validating code "pal" an ancient language, which probably will '
511 . 'not appear in Names.php, but appears in CLDR in English'
516 * Negative tests for Language::isKnownLanguageTag()
517 * @dataProvider provideUnKnownLanguageTags
518 * @covers Language::isKnownLanguageTag
520 public function testUnknownLanguageTag( $code, $message = '' ) {
522 (bool)Language
::isKnownLanguageTag( $code ),
523 "checking that code $code is invalid - $message"
527 public static function provideUnknownLanguageTags() {
529 array( 'mw', 'non-existent two-letter code' ),
530 array( 'foo"<bar', 'very invalid language code' ),
535 * Test too short timestamp
536 * @expectedException MWException
537 * @covers Language::sprintfDate
539 public function testSprintfDateTooShortTimestamp() {
540 $this->getLang()->sprintfDate( 'xiY', '1234567890123' );
544 * Test too long timestamp
545 * @expectedException MWException
546 * @covers Language::sprintfDate
548 public function testSprintfDateTooLongTimestamp() {
549 $this->getLang()->sprintfDate( 'xiY', '123456789012345' );
553 * Test too short timestamp
554 * @expectedException MWException
555 * @covers Language::sprintfDate
557 public function testSprintfDateNotAllDigitTimestamp() {
558 $this->getLang()->sprintfDate( 'xiY', '-1234567890123' );
562 * @dataProvider provideSprintfDateSamples
563 * @covers Language::sprintfDate
565 public function testSprintfDate( $format, $ts, $expected, $msg ) {
569 $this->getLang()->sprintfDate( $format, $ts, null, $ttl ),
570 "sprintfDate('$format', '$ts'): $msg"
573 $dt = new DateTime( $ts );
574 $lastValidTS = $dt->add( new DateInterval( 'PT' . ( $ttl - 1 ) . 'S' ) )->format( 'YmdHis' );
577 $this->getLang()->sprintfDate( $format, $lastValidTS, null ),
578 "sprintfDate('$format', '$ts'): TTL $ttl too high (output was different at $lastValidTS)"
581 // advance the time enough to make all of the possible outputs different (except possibly L)
582 $dt = new DateTime( $ts );
583 $newTS = $dt->add( new DateInterval( 'P1Y1M8DT13H1M1S' ) )->format( 'YmdHis' );
586 $this->getLang()->sprintfDate( $format, $newTS, null ),
587 "sprintfDate('$format', '$ts'): Missing TTL (output was different at $newTS)"
593 * sprintfDate should always use UTC when no zone is given.
594 * @dataProvider provideSprintfDateSamples
595 * @covers Language::sprintfDate
597 public function testSprintfDateNoZone( $format, $ts, $expected, $ignore, $msg ) {
598 $oldTZ = date_default_timezone_get();
599 $res = date_default_timezone_set( 'Asia/Seoul' );
601 $this->markTestSkipped( "Error setting Timezone" );
606 $this->getLang()->sprintfDate( $format, $ts ),
607 "sprintfDate('$format', '$ts'): $msg"
610 date_default_timezone_set( $oldTZ );
614 * sprintfDate should use passed timezone
615 * @dataProvider provideSprintfDateSamples
616 * @covers Language::sprintfDate
618 public function testSprintfDateTZ( $format, $ts, $ignore, $expected, $msg ) {
619 $tz = new DateTimeZone( 'Asia/Seoul' );
621 $this->markTestSkipped( "Error getting Timezone" );
626 $this->getLang()->sprintfDate( $format, $ts, $tz ),
627 "sprintfDate('$format', '$ts', 'Asia/Seoul'): $msg"
631 public static function provideSprintfDateSamples() {
636 '1390', // note because we're testing English locale we get Latin-standard digits
638 'Iranian calendar full year'
645 'Iranian calendar short year'
652 'ISO 8601 (week) year'
675 // What follows is mostly copied from
676 // https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
703 'Month index, not zero pad'
710 'Month index. Zero pad'
731 'Genitive month name (same in EN)'
738 'Day of month (not zero pad)'
745 'Day of month (zero-pad)'
752 'Day of year (zero-indexed)'
759 'Day of week (abbrev)'
773 'Day of week (Mon=1, Sun=7)'
780 'Day of week (Sun=0, Sat=6)'
822 '12 hour, zero padded'
871 'Days in current month'
876 '2012-01-02T09:07:05+00:00',
877 '2012-01-02T09:07:05+09:00',
883 'Mon, 02 Jan 2012 09:07:05 +0000',
884 'Mon, 02 Jan 2012 09:07:05 +0900',
892 'Timezone identifier'
913 'Timezone offset with colon'
920 'Timezone abbreviation'
927 'Timezone offset in seconds'
955 'Hebrew number of days in month'
962 'Hebrew genitive month name (No difference in EN)'
1004 'Raw numerals (doesn\'t mean much in EN)'
1007 '[[Y "(yea"\\r)]] \\"xx\\"',
1009 '[[2012 (year)]] "x"',
1010 '[[2012 (year)]] "x"',
1018 * @dataProvider provideFormatSizes
1019 * @covers Language::formatSize
1021 public function testFormatSize( $size, $expected, $msg ) {
1022 $this->assertEquals(
1024 $this->getLang()->formatSize( $size ),
1025 "formatSize('$size'): $msg"
1029 public static function provideFormatSizes() {
1076 // How big!? THIS BIG!
1081 * @dataProvider provideFormatBitrate
1082 * @covers Language::formatBitrate
1084 public function testFormatBitrate( $bps, $expected, $msg ) {
1085 $this->assertEquals(
1087 $this->getLang()->formatBitrate( $bps ),
1088 "formatBitrate('$bps'): $msg"
1092 public static function provideFormatBitrate() {
1102 "999 bits per second"
1107 "1 kilobit per second"
1112 "1 megabit per second"
1117 "1 gigabit per second"
1122 "1 terabit per second"
1127 "1 petabit per second"
1132 "1 exabit per second"
1137 "1 zetabit per second"
1142 "1 yottabit per second"
1147 "1,000 yottabits per second"
1153 * @dataProvider provideFormatDuration
1154 * @covers Language::formatDuration
1156 public function testFormatDuration( $duration, $expected, $intervals = array() ) {
1157 $this->assertEquals(
1159 $this->getLang()->formatDuration( $duration, $intervals ),
1160 "formatDuration('$duration'): $expected"
1164 public static function provideFormatDuration() {
1203 // ( 365 + ( 24 * 3 + 25 ) / 400 ) * 86400 = 31556952
1204 ( 365 +
( 24 * 3 +
25 ) / 400.0 ) * 86400,
1237 '2 hours, 30 minutes and 1 second'
1241 '1 hour and 1 second'
1244 31556952 +
2 * 86400 +
9000,
1245 '1 year, 2 days, 2 hours and 30 minutes'
1248 42 * 1000 * 31556952 +
42,
1249 '42 millennia and 42 seconds'
1267 31556952 +
2 * 86400 +
9000,
1268 '1 year, 2 days and 150 minutes',
1269 array( 'years', 'days', 'minutes' ),
1274 array( 'years', 'days' ),
1277 31556952 +
2 * 86400 +
9000,
1278 '1 year, 2 days and 150 minutes',
1279 array( 'minutes', 'days', 'years' ),
1284 array( 'days', 'years' ),
1290 * @dataProvider provideCheckTitleEncodingData
1291 * @covers Language::checkTitleEncoding
1293 public function testCheckTitleEncoding( $s ) {
1294 $this->assertEquals(
1296 $this->getLang()->checkTitleEncoding( $s ),
1297 "checkTitleEncoding('$s')"
1301 public static function provideCheckTitleEncodingData() {
1302 // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong
1305 array( "United States of America" ), // 7bit ASCII
1306 array( rawurldecode( "S%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e" ) ),
1309 "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn"
1312 // The following two data sets come from bug 36839. They fail if checkTitleEncoding uses a regexp to test for
1313 // valid UTF-8 encoding and the pcre.recursion_limit is low (like, say, 1024). They succeed if checkTitleEncoding
1314 // uses mb_check_encoding for its test.
1317 "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn%7C"
1318 . "Catherine%20Willows%7CDavid%20Hodges%7CDavid%20Phillips%7CGil%20Grissom%7CGreg%20Sanders%7CHodges%7C"
1319 . "Internet%20Movie%20Database%7CJim%20Brass%7CLady%20Heather%7C"
1320 . "Les%20Experts%20(s%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e)%7CLes%20Experts%20:%20Manhattan%7C"
1321 . "Les%20Experts%20:%20Miami%7CListe%20des%20personnages%20des%20Experts%7C"
1322 . "Liste%20des%20%C3%A9pisodes%20des%20Experts%7CMod%C3%A8le%20discussion:Palette%20Les%20Experts%7C"
1323 . "Nick%20Stokes%7CPersonnage%20de%20fiction%7CPersonnage%20fictif%7CPersonnage%20de%20fiction%7C"
1324 . "Personnages%20r%C3%A9currents%20dans%20Les%20Experts%7CRaymond%20Langston%7CRiley%20Adams%7C"
1325 . "Saison%201%20des%20Experts%7CSaison%2010%20des%20Experts%7CSaison%2011%20des%20Experts%7C"
1326 . "Saison%2012%20des%20Experts%7CSaison%202%20des%20Experts%7CSaison%203%20des%20Experts%7C"
1327 . "Saison%204%20des%20Experts%7CSaison%205%20des%20Experts%7CSaison%206%20des%20Experts%7C"
1328 . "Saison%207%20des%20Experts%7CSaison%208%20des%20Experts%7CSaison%209%20des%20Experts%7C"
1329 . "Sara%20Sidle%7CSofia%20Curtis%7CS%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e%7CWallace%20Langham%7C"
1330 . "Warrick%20Brown%7CWendy%20Simms%7C%C3%89tats-Unis"
1335 "Mod%C3%A8le%3AArrondissements%20homonymes%7CMod%C3%A8le%3ABandeau%20standard%20pour%20page%20d'homonymie%7C"
1336 . "Mod%C3%A8le%3ABatailles%20homonymes%7CMod%C3%A8le%3ACantons%20homonymes%7C"
1337 . "Mod%C3%A8le%3ACommunes%20fran%C3%A7aises%20homonymes%7CMod%C3%A8le%3AFilms%20homonymes%7C"
1338 . "Mod%C3%A8le%3AGouvernements%20homonymes%7CMod%C3%A8le%3AGuerres%20homonymes%7CMod%C3%A8le%3AHomonymie%7C"
1339 . "Mod%C3%A8le%3AHomonymie%20bateau%7CMod%C3%A8le%3AHomonymie%20d'%C3%A9tablissements%20scolaires%20ou"
1340 . "%20universitaires%7CMod%C3%A8le%3AHomonymie%20d'%C3%AEles%7CMod%C3%A8le%3AHomonymie%20de%20clubs%20sportifs%7C"
1341 . "Mod%C3%A8le%3AHomonymie%20de%20comt%C3%A9s%7CMod%C3%A8le%3AHomonymie%20de%20monument%7C"
1342 . "Mod%C3%A8le%3AHomonymie%20de%20nom%20romain%7CMod%C3%A8le%3AHomonymie%20de%20parti%20politique%7C"
1343 . "Mod%C3%A8le%3AHomonymie%20de%20route%7CMod%C3%A8le%3AHomonymie%20dynastique%7C"
1344 . "Mod%C3%A8le%3AHomonymie%20vid%C3%A9oludique%7CMod%C3%A8le%3AHomonymie%20%C3%A9difice%20religieux%7C"
1345 . "Mod%C3%A8le%3AInternationalisation%7CMod%C3%A8le%3AIsom%C3%A9rie%7CMod%C3%A8le%3AParonymie%7C"
1346 . "Mod%C3%A8le%3APatronyme%7CMod%C3%A8le%3APatronyme%20basque%7CMod%C3%A8le%3APatronyme%20italien%7C"
1347 . "Mod%C3%A8le%3APatronymie%7CMod%C3%A8le%3APersonnes%20homonymes%7CMod%C3%A8le%3ASaints%20homonymes%7C"
1348 . "Mod%C3%A8le%3ATitres%20homonymes%7CMod%C3%A8le%3AToponymie%7CMod%C3%A8le%3AUnit%C3%A9s%20homonymes%7C"
1349 . "Mod%C3%A8le%3AVilles%20homonymes%7CMod%C3%A8le%3A%C3%89difices%20religieux%20homonymes"
1353 // @codingStandardsIgnoreEnd
1357 * @dataProvider provideRomanNumeralsData
1358 * @covers Language::romanNumeral
1360 public function testRomanNumerals( $num, $numerals ) {
1361 $this->assertEquals(
1363 Language
::romanNumeral( $num ),
1364 "romanNumeral('$num')"
1368 public static function provideRomanNumeralsData() {
1383 array( 49, 'XLIX' ),
1387 array( 80, 'LXXX' ),
1389 array( 99, 'XCIX' ),
1392 array( 300, 'CCC' ),
1396 array( 700, 'DCC' ),
1397 array( 800, 'DCCC' ),
1399 array( 999, 'CMXCIX' ),
1401 array( 1989, 'MCMLXXXIX' ),
1402 array( 2000, 'MM' ),
1403 array( 3000, 'MMM' ),
1404 array( 4000, 'MMMM' ),
1405 array( 5000, 'MMMMM' ),
1406 array( 6000, 'MMMMMM' ),
1407 array( 7000, 'MMMMMMM' ),
1408 array( 8000, 'MMMMMMMM' ),
1409 array( 9000, 'MMMMMMMMM' ),
1410 array( 9999, 'MMMMMMMMMCMXCIX' ),
1411 array( 10000, 'MMMMMMMMMM' ),
1416 * @dataProvider provideHebrewNumeralsData
1417 * @covers Language::hebrewNumeral
1419 public function testHebrewNumeral( $num, $numerals ) {
1420 $this->assertEquals(
1422 Language
::hebrewNumeral( $num ),
1423 "hebrewNumeral('$num')"
1427 public static function provideHebrewNumeralsData() {
1457 array( 101, 'ק"א' ),
1458 array( 110, 'ק"י' ),
1462 array( 500, 'ת"ק' ),
1463 array( 800, 'ת"ת' ),
1464 array( 1000, "א' אלף" ),
1465 array( 1001, "א'א'" ),
1466 array( 1012, "א'י\"ב" ),
1467 array( 1020, "א'ך'" ),
1468 array( 1030, "א'ל'" ),
1469 array( 1081, "א'פ\"א" ),
1470 array( 2000, "ב' אלפים" ),
1471 array( 2016, "ב'ט\"ז" ),
1472 array( 3000, "ג' אלפים" ),
1473 array( 4000, "ד' אלפים" ),
1474 array( 4904, "ד'תתק\"ד" ),
1475 array( 5000, "ה' אלפים" ),
1476 array( 5680, "ה'תר\"ף" ),
1477 array( 5690, "ה'תר\"ץ" ),
1478 array( 5708, "ה'תש\"ח" ),
1479 array( 5720, "ה'תש\"ך" ),
1480 array( 5740, "ה'תש\"ם" ),
1481 array( 5750, "ה'תש\"ן" ),
1482 array( 5775, "ה'תשע\"ה" ),
1487 * @dataProvider providePluralData
1488 * @covers Language::convertPlural
1490 public function testConvertPlural( $expected, $number, $forms ) {
1491 $chosen = $this->getLang()->convertPlural( $number, $forms );
1492 $this->assertEquals( $expected, $chosen );
1495 public static function providePluralData() {
1496 // Params are: [expected text, number given, [the plural forms]]
1498 array( 'plural', 0, array(
1499 'singular', 'plural'
1501 array( 'explicit zero', 0, array(
1502 '0=explicit zero', 'singular', 'plural'
1504 array( 'explicit one', 1, array(
1505 'singular', 'plural', '1=explicit one',
1507 array( 'singular', 1, array(
1508 'singular', 'plural', '0=explicit zero',
1510 array( 'plural', 3, array(
1511 '0=explicit zero', '1=explicit one', 'singular', 'plural'
1513 array( 'explicit eleven', 11, array(
1514 'singular', 'plural', '11=explicit eleven',
1516 array( 'plural', 12, array(
1517 'singular', 'plural', '11=explicit twelve',
1519 array( 'plural', 12, array(
1520 'singular', 'plural', '=explicit form',
1522 array( 'other', 2, array(
1523 'kissa=kala', '1=2=3', 'other',
1525 array( '', 2, array(
1526 '0=explicit zero', '1=explicit one',
1532 * @covers Language::embedBidi()
1534 public function testEmbedBidi() {
1535 $lre = "\xE2\x80\xAA"; // U+202A LEFT-TO-RIGHT EMBEDDING
1536 $rle = "\xE2\x80\xAB"; // U+202B RIGHT-TO-LEFT EMBEDDING
1537 $pdf = "\xE2\x80\xAC"; // U+202C POP DIRECTIONAL FORMATTING
1538 $lang = $this->getLang();
1539 $this->assertEquals(
1541 $lang->embedBidi( '123' ),
1542 'embedBidi with neutral argument'
1544 $this->assertEquals(
1545 $lre . 'Ben_(WMF)' . $pdf,
1546 $lang->embedBidi( 'Ben_(WMF)' ),
1547 'embedBidi with LTR argument'
1549 $this->assertEquals(
1550 $rle . 'יהודי (מנוחין)' . $pdf,
1551 $lang->embedBidi( 'יהודי (מנוחין)' ),
1552 'embedBidi with RTL argument'
1557 * @covers Language::translateBlockExpiry()
1558 * @dataProvider provideTranslateBlockExpiry
1560 public function testTranslateBlockExpiry( $expectedData, $str, $desc ) {
1561 $lang = $this->getLang();
1562 if ( is_array( $expectedData ) ) {
1563 list( $func, $arg ) = $expectedData;
1564 $expected = $lang->$func( $arg );
1566 $expected = $expectedData;
1568 $this->assertEquals( $expected, $lang->translateBlockExpiry( $str ), $desc );
1571 public static function provideTranslateBlockExpiry() {
1573 array( '2 hours', '2 hours', 'simple data from ipboptions' ),
1574 array( 'indefinite', 'infinite', 'infinite from ipboptions' ),
1575 array( 'indefinite', 'infinity', 'alternative infinite from ipboptions' ),
1576 array( 'indefinite', 'indefinite', 'another alternative infinite from ipboptions' ),
1577 array( array( 'formatDuration', 1023 * 60 * 60 ), '1023 hours', 'relative' ),
1578 array( array( 'formatDuration', -1023 ), '-1023 seconds', 'negative relative' ),
1579 array( array( 'formatDuration', 0 ), 'now', 'now' ),
1581 array( 'timeanddate', '20120102070000' ),
1582 '2012-1-1 7:00 +1 day',
1583 'mixed, handled as absolute'
1585 array( array( 'timeanddate', '19910203040506' ), '1991-2-3 4:05:06', 'absolute' ),
1586 array( array( 'timeanddate', '19700101000000' ), '1970-1-1 0:00:00', 'absolute at epoch' ),
1587 array( array( 'timeanddate', '19691231235959' ), '1969-12-31 23:59:59', 'time before epoch' ),
1588 array( 'dummy', 'dummy', 'return garbage as is' ),
1593 * @dataProvider parseFormattedNumberProvider
1595 public function testParseFormattedNumber( $langCode, $number ) {
1596 $lang = Language
::factory( $langCode );
1598 $localisedNum = $lang->formatNum( $number );
1599 $normalisedNum = $lang->parseFormattedNumber( $localisedNum );
1601 $this->assertEquals( $number, $normalisedNum );
1604 public function parseFormattedNumberProvider() {
1606 array( 'de', 377.01 ),
1608 array( 'fa', 382.772 ),
1609 array( 'ar', 1844 ),
1610 array( 'lzh', 3731 ),
1611 array( 'zh-classical', 7432 )
1616 * @covers Language::commafy()
1617 * @dataProvider provideCommafyData
1619 public function testCommafy( $number, $numbersWithCommas ) {
1620 $this->assertEquals(
1622 $this->getLang()->commafy( $number ),
1623 "commafy('$number')"
1627 public static function provideCommafyData() {
1631 array( 100, '100' ),
1632 array( 1000, '1,000' ),
1633 array( 10000, '10,000' ),
1634 array( 100000, '100,000' ),
1635 array( 1000000, '1,000,000' ),
1636 array( -1.0001, '-1.0001' ),
1637 array( 1.0001, '1.0001' ),
1638 array( 10.0001, '10.0001' ),
1639 array( 100.0001, '100.0001' ),
1640 array( 1000.0001, '1,000.0001' ),
1641 array( 10000.0001, '10,000.0001' ),
1642 array( 100000.0001, '100,000.0001' ),
1643 array( 1000000.0001, '1,000,000.0001' ),
1644 array( '200000000000000000000', '200,000,000,000,000,000,000' ),
1645 array( '-200000000000000000000', '-200,000,000,000,000,000,000' ),
1650 * @covers Language::listToText
1652 public function testListToText() {
1653 $lang = $this->getLang();
1654 $and = $lang->getMessageFromDB( 'and' );
1655 $s = $lang->getMessageFromDB( 'word-separator' );
1656 $c = $lang->getMessageFromDB( 'comma-separator' );
1658 $this->assertEquals( '', $lang->listToText( array() ) );
1659 $this->assertEquals( 'a', $lang->listToText( array( 'a' ) ) );
1660 $this->assertEquals( "a{$and}{$s}b", $lang->listToText( array( 'a', 'b' ) ) );
1661 $this->assertEquals( "a{$c}b{$and}{$s}c", $lang->listToText( array( 'a', 'b', 'c' ) ) );
1662 $this->assertEquals( "a{$c}b{$c}c{$and}{$s}d", $lang->listToText( array( 'a', 'b', 'c', 'd' ) ) );
1666 * @dataProvider provideIsSupportedLanguage
1667 * @covers Language::isSupportedLanguage
1669 public function testIsSupportedLanguage( $code, $expected, $comment ) {
1670 $this->assertEquals( $expected, Language
::isSupportedLanguage( $code ), $comment );
1673 public static function provideIsSupportedLanguage() {
1675 array( 'en', true, 'is supported language' ),
1676 array( 'fi', true, 'is supported language' ),
1677 array( 'bunny', false, 'is not supported language' ),
1678 array( 'FI', false, 'is not supported language, input should be in lower case' ),
1683 * @dataProvider provideGetParentLanguage
1684 * @covers Language::getParentLanguage
1686 public function testGetParentLanguage( $code, $expected, $comment ) {
1687 $lang = Language
::factory( $code );
1688 if ( is_null( $expected ) ) {
1689 $this->assertNull( $lang->getParentLanguage(), $comment );
1691 $this->assertEquals( $expected, $lang->getParentLanguage()->getCode(), $comment );
1695 public static function provideGetParentLanguage() {
1697 array( 'zh-cn', 'zh', 'zh is the parent language of zh-cn' ),
1698 array( 'zh', 'zh', 'zh is defined as the parent language of zh, '
1699 . 'because zh converter can convert zh-cn to zh' ),
1700 array( 'zh-invalid', null, 'do not be fooled by arbitrarily composed language codes' ),
1701 array( 'en-gb', null, 'en does not have converter' ),
1702 array( 'en', null, 'en does not have converter. Although FakeConverter '
1703 . 'handles en -> en conversion but it is useless' ),
1708 * @dataProvider provideGetNamespaceAliases
1709 * @covers Language::getNamespaceAliases
1711 public function testGetNamespaceAliases( $languageCode, $subset ) {
1712 $language = Language
::factory( $languageCode );
1713 $aliases = $language->getNamespaceAliases();
1714 foreach ( $subset as $alias => $nsId ) {
1715 $this->assertEquals( $nsId, $aliases[$alias] );
1719 public static function provideGetNamespaceAliases() {
1720 // TODO: Add tests for NS_PROJECT_TALK and GenderNamespaces